/*
 * Copyright (c) 2022  by Junzhao
 * THIS FILE IS PART OF JAVASE Song Huongkang PROJECT
 * All Rights Reserved.
 */

package 动态代理;

import 动态代理.回顾静态代理.ClothFactory;
import 动态代理.回顾静态代理.JZClothFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface Human {
    String getBelief();

    void eat(String food);
}

/**
 * @Description 动态代理
 * 代理设计模式的原理 :
 * 使用一个代理将对象包装起来 , 然后用该代理对象取代原始对象。任何对原
 * 始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原
 * 始对象上 。
 * <p>
 * 之前为大家讲解过代理机制的操作，属于静态代理，特征是代理类和目标
 * 对象的类都是在编译期间确定下来，不利于程序的扩展。同时，每一个代
 * 理类只能为一个接口服务，这样一来程序开发中必然产生过多的代理。 最
 * 好可以通过一个代理类完成全部的代理功能
 * <p>
 * 动态代理是指客户通过代理类来调用其它对象的 方法，并且是在程序运行时
 * 根据需要动态创建目标类的代理对象。
 * <p>
 * 动态代理使用场合
 * 调试
 * 远程 方法 调用
 * 动态代理相比于静态代理的优点：
 * 抽象角色中（接口）声明的所有方法都被转移到调用处理器一个集中的方法中
 * 处理，这样，我们可以更加灵活和统一的处理众多的方法。
 * <p>
 * Proxy ：专门完成代理的 操作类，是所有动态代理类的父 类。通过此类为一
 * 个或多个接口动态地生成实现类。
 * 提供 用于创建动态代理类 和动态代理 对象的静态 方法
 * <p>
 * static Class <?> getProxyClass( ClassLoader loader, Class<?>... interfaces )
 * 创建一个动态代理类所对应的 Class 对象
 * <p>
 * static Object newProxyInstance( ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
 * 直接创建一个动态代理对象
 * <p>
 * 1.创建一个实现接口 InvocationHandler 的类，它必须实现 invoke 方法，以完成代理的具体操作。
 * public Object invoke(Object theProxy , Method method , Object[] params ) throws Throwable{
 * try{                   代理类的对象       要调用的方法方法    调用时所需要的参数
 * Object returnVal = method.invoke( targetObj , params)
 * System.out.println(returnVal);
 * return returnVal;
 * }catch(Exception exc){}
 * }
 * @Author 俊昭
 * @Date 2022/5/11
 */
public class DynamicProxyTest {
    public static void main(String[] args) {
        SpiderMan spiderMan = new SpiderMan();
        // 此时instance 就是代理类对象
        Object instance = ProxyFactory.getInstance(spiderMan);

        Human human = (Human) instance;

        human.eat("noodles");

        String belief = human.getBelief();
        System.out.println(belief);

//        SpiderMan eats noodles .
//With great power comes great responsibility !

        // 验证动态性

        JZClothFactory factory = new JZClothFactory();

        instance = ProxyFactory.getInstance(factory);

        ClothFactory clothFactory = (ClothFactory) instance;

        clothFactory.produceCloth();
        // 被代理工厂生产衣服


    }
}

/**
 * 实现动态代理要解决的问题
 * 一 根据加载到内存中的被代理类动态创建代理类的对象
 * 二 通过代理类的对象动态调用被代理类同名的方法
 */
class ProxyFactory {
    // 得到代理类实例
    public static Object getInstance(Object obj) {

        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);

        Class<?> aClass = obj.getClass();

        return Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {

    private Object obj;// 赋值事实也需要用被代理类的对象进行赋值

    public void bind(Object obj) {
        this.obj = obj;
    }

    /**
     * 通过代理类对象调用方法时 就会调用这个方法
     * <p>
     * 将被代理类要执行的功能声明在invoke方法中
     * <p>
     * * public Object invoke(Object theProxy , Method method , Object[] params ) throws Throwable{
     * * try{                   代理类的对象       要调用的方法方法    调用时所需要的参数
     * *     Object returnVal = method.invoke( targetObj , params)
     * *     System.out.println(returnVal);
     * *     return returnVal;
     * * }catch(Exception exc){}
     * * }
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 此时这个方法就是代理的类对象调用的方法 也就是被代理类要调用的方法

        //        method.setAccessible(true);

        return method.invoke(obj, args);
    }
}

class SpiderMan implements Human {
    @Override
    public String getBelief() {
        return "With great power comes great responsibility !";
    }

    @Override
    public void eat(String food) {
        System.out.println("SpiderMan eats " + food + " .");
    }
}